package cn.tedu.st.security.filter;

import cn.tedu.st.base.exception.CustomerAuthenticationException;
import cn.tedu.st.base.handler.LoginFailureHandler;
import cn.tedu.st.security.pojo.vo.UserDetailsImpl;
import cn.tedu.st.base.util.JwtUtil;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义过滤器，用来拦截JWT认证请求
 * 每个请求只过一遍
 */

@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.debug("JwtAuthenticationTokenFilter过滤器执行了");

        try {
            String requestURI = request.getRequestURI();
            log.debug("请求的URI：{}", requestURI);
            if (!requestURI.equals("/auth/login")&&!requestURI.equals("/to_login")) {
                //如果请求不是登录请求，则校验令牌--------->进入表单校验处理器
                this.volatileToken(request);
            }
        } catch (AuthenticationException e) {
            log.debug("JwtAuthenticationTokenFilter过滤器抛出的异常：{}", e.getMessage());
            loginFailureHandler.onAuthenticationFailure(request, response, e);
            return;
        }
        //放行
        filterChain.doFilter(request, response);
        log.debug("JwtAuthenticationTokenFilter过滤器执行完毕");
    }

    //用于token的校验
    private void volatileToken(HttpServletRequest request){
        log.debug("进入校验token方法");
        //从请求头中获取jwt令牌
        String token = request.getHeader("Authorization");
        //判断token是否为空字符串
        if (ObjectUtils.isEmpty(token)) {
            //如果没请求头中没有名为Authorization的文本，则从参数中获取jwt
            token=request.getParameter("Authorization");
        }
        //判断token是否为空, 如果为空字符串，则抛出异常
        if (ObjectUtils.isEmpty(token)){
            throw new CustomerAuthenticationException("token为空");
        }

        //去解析token令牌
        UserDetailsImpl userDetailsImpl=null;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            log.debug("解析token令牌：{}", claims);
            //4.从claims中获取用户信息
            String subject = claims.getSubject();
            log.debug("从解析好的claims中获取的用户信息：{}", subject);
            //5.将用户信息存入userDetails中
            userDetailsImpl = JSON.parseObject(subject, UserDetailsImpl.class);
            log.debug("从claims中获取用户信息序列化成UserDetailsImpl对象：{}", userDetailsImpl);
        } catch (Exception e) {
            throw new CustomerAuthenticationException("token解析失败");
        }

        //---------redis的校验，查看是否过期
        String redisStr = redisTemplate.opsForValue().get("token_" + token);
        log.debug("redis中获取的token：{}", redisStr);
        if (ObjectUtils.isEmpty(redisStr)){
            throw new CustomerAuthenticationException("token已过期");
        }

        log.debug("用户拥有的权限：{}",userDetailsImpl.getAuthorities());
        //6.把用户信息存入SecurityContext中
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetailsImpl, null, userDetailsImpl.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        log.debug("将用户信息存入SecurityContextHolder中：{}", authenticationToken);
        log.debug("结束校验token方法");
    }
}
